﻿using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace Interface.Visualization
{
    public class GLCamera
    {
        private Vector3d _position = new Vector3d();
        private Vector3d _direction = new Vector3d(0, 0, -1);
        private Vector3d _up = Vector3d.UnitY;
        private bool _sideCached;
        private Vector3d _side;

        public Vector3d Position { get { return _position; } }
        public Vector3d Direction { get { return _direction; } }
        public Vector3d Interest { get { return _position + _direction; } }
        public Vector3d Up { get { return _up; } }
        public Vector3d Side
        {
            get
            {
                if (!_sideCached)
                {
                    Vector3d.Cross(ref _up, ref _direction, out _side);
                    _sideCached = true;
                }

                return _side;
            }
        }

        public GLCamera()
        {
            _position = new Vector3d(0, 0, 0);
            _direction = -Vector3d.UnitZ;
            _up = Vector3d.UnitY;

            Vector3d.Cross(ref _up, ref _direction, out _side);
            _sideCached = true;
        }

        public GLCamera(Vector3d p, Vector3d d, Vector3d u)
        {
            _position = p;
            _direction = d;
            _up = u;

            _direction.Normalize();
            _up.Normalize();
            Vector3d.Cross(ref _up, ref _direction, out _side);
            _sideCached = true;
        }

        public void MoveIn(float d)
        {
            _position += d * _direction;
        }

        public void MoveUp(float d)
        {
            _position += d * _up;
        }

        public void MoveSide(float d)
        {
            _position += d * Side;
        }

        // if you are facing x:
        //   roll is around x
        //   yaw is around y
        //   pitch is around z
        public void Roll(float a)
        {
            Matrix4d rot = Matrix4d.CreateFromAxisAngle(_direction, a);
            Vector3d.Transform(ref _up, ref rot, out _up);
            _sideCached = false;
        }

        public void Yaw(float a)
        {
            Matrix4d rot = Matrix4d.CreateFromAxisAngle(_up, a);
            Vector3d.Transform(ref _direction, ref rot, out _direction);
            _sideCached = false;
        }

        public void Pitch(float a)
        {
            Matrix4d rot = Matrix4d.CreateFromAxisAngle(Side, a);
            Vector3d.Transform(ref _direction, ref rot, out _direction);
            Vector3d.Transform(ref _up, ref rot, out _up);
        }

        public override string ToString()
        {
            return _position + "  " + _direction + "  " + _up;
        }
    }
}
